package com.njupt.simulation.utils;

import net.sf.geographiclib.Geodesic;
import net.sf.geographiclib.GeodesicData;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 泊松分布类，用于在指定圆形区域内根据泊松分布生成随机点
 */
public class PoissonDemo {

    // 圆心的经纬度坐标（单位：度）
    private double centerLat;
    private double centerLon;

    // 圆的半径（单位：公里）
    private double radius;

    // 在圆形区域内随机撒点的密度（单位：点数/平方公里）
    private double density;

    // 随机数生成器
    private Random random;

    /**
     * 构造函数，初始化泊松分布类的参数
     * @param centerLat 圆心的纬度
     * @param centerLon 圆心的经度
     * @param radius 圆的半径（单位：公里）
     * @param density 在圆形区域内随机撒点的密度（单位：点数/平方公里）
     */
    public PoissonDemo(double centerLat, double centerLon, double radius, double density) {
        this.centerLat = centerLat;
        this.centerLon = centerLon;
        this.radius = radius;
        this.density = density;
        this.random = new Random();
    }

    /**
     * 根据泊松分布生成随机点数
     * @param lambda 泊松分布的期望值
     * @return 生成的随机点数
     */
    private int poissonRandom(double lambda) {
        int k = 0;
        double p = 1.0;
        double L = Math.exp(-lambda);
        do {
            k++;
            p *= random.nextDouble();
        } while (p > L);
        return k - 1;
    }

    /**
     * 在圆形区域内生成均匀分布的随机点
     * @return 随机点的列表，每个点用经纬度表示
     */
    private List<double[]> generateRandomPoints() {
        List<double[]> points = new ArrayList<>();
        double radiusMeters = radius * 1000;
        double areaMeters2 = Math.PI * radiusMeters * radiusMeters;
        // 期望的点的数量（泊松分布的λ参数）
        double lambda = density * (areaMeters2 / 1_000_000); // 将平方米转换为平方公里
        int numPoints = poissonRandom(lambda);

        for (int i = 0; i < lambda; i++) {
            double r = radius * Math.sqrt(random.nextDouble());
            double theta = 2 * Math.PI * random.nextDouble();

            double distance = r * 1000; // 转换为米

            // 将平面坐标转换为经纬度坐标
            double[] latLon = calculateRandomPoint(centerLat, centerLon, distance, theta);
            // 增加高度变量，默认为0
            double[] latLonHeight = new double[]{latLon[0], latLon[1], 0.0};
            points.add(latLonHeight);
        }

        return points;
    }

    /**
     * 使用GeographicLib计算随机点的经纬度
     * @param centerLat 圆心的纬度
     * @param centerLon 圆心的经度
     * @param distance 随机点到圆心的距离，单位为米
     * @param angle 随机点相对于圆心的角度，单位为弧度
     * @return 随机点的经纬度坐标
     */
    private double[] calculateRandomPoint(double centerLat, double centerLon, double distance, double angle) {
        GeodesicData gd = Geodesic.WGS84.Direct(centerLat, centerLon, Math.toDegrees(angle), distance);
        return new double[]{gd.lat2, gd.lon2};
    }

    /**
     * 显示生成的随机点的经纬度坐标
     */
    public void displayPoints1() {
        List<double[]> points = generateRandomPoints();

        for (double[] point : points) {
            System.out.printf("Latitude: %.6f, Longitude: %.6f%n", point[0], point[1]);
        }
        System.out.println("生成的随机点个数: " + points.size()); // 打印点数个数
    }

    public static void main(String[] args) {
        double centerLat = 29; // 圆心的纬度
        double centerLon = 49; // 圆心的经度
        double radius = 100; // 半径（单位：公里）
        double density = 100; // 密度（单位：点数/平方公里）

        PoissonDemo poissonPoints = new PoissonDemo(centerLat, centerLon, radius, density);
        poissonPoints.displayPoints1();
    }
}